home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Multimedia / Resource Library: Multimedia.iso / utils / sound / convrtr / amiga / amisox_w / sox.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-12  |  16.0 KB  |  702 lines

  1. /*
  2.  * July 5, 1991
  3.  * Copyright 1991 Lance Norskog And Sundry Contributors
  4.  * This source code is freely redistributable and may be used for
  5.  * any purpose.  This copyright notice must be maintained. 
  6.  * Lance Norskog And Sundry Contributors are not responsible for 
  7.  * the consequences of using this software.
  8.  */
  9.  
  10. #include "st.h"
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #ifdef AMIGA
  14. #include "varargs.h"
  15. #else
  16. #include <varargs.h>
  17. #endif AMIGA
  18. #include <ctype.h>
  19. #include <string.h>
  20.  
  21. /*
  22.  * SOX main program.
  23.  *
  24.  * Rewrite for new nicer option syntax.  July 13, 1991.
  25.  * Rewrite for separate effects library.  Sep. 15, 1991.
  26.  */
  27.  
  28. #ifdef AMIGA
  29. #include "patchlevel.h"
  30. char amiversion[AmiVerSize]=AmiVerChars;
  31. #endif AMIGA
  32.  
  33. float volume = 1.0;    /* expansion coefficient */
  34. int dovolume = 0;
  35. int clipped = 0;    /* Volume change clipping errors */
  36.  
  37. float amplitude = 1.0;    /* Largest sample so far in intermediate buffer */
  38.  
  39. int writing = 0;    /* are we writing to a file? */
  40. int verbose = 0;    /* be noisy on stderr */
  41.  
  42. long ibuf[BUFSIZ];    /* Intermediate processing buffer */
  43. long obuf[BUFSIZ];    /* Intermediate processing buffer */
  44.  
  45. long volumechange();
  46.  
  47. #ifdef    DOS
  48. char writebuf[BUFSIZ];    /* output write buffer */
  49. #endif
  50.  
  51. void    gettype(), report(), geteffect();
  52.  
  53. struct soundstream informat, outformat;
  54.  
  55. char *myname;
  56. extern errno;
  57. extern char *sys_errlist[];
  58.  
  59. ft_t ft;
  60. struct effect eff;
  61. char *ifile, *ofile, *itype, *otype;
  62. extern char *optarg;
  63. extern int optind;
  64.  
  65. main(n, args)
  66. int n;
  67. char **args;
  68. {
  69.     myname = args[0];
  70.     init();
  71.     
  72.     ifile = ofile = NULL;
  73.  
  74.     /* Get input format options */
  75.     ft = &informat;
  76.     doopts(n, args);
  77.     /* Get input file */
  78.     if (optind >= n)
  79.         usage("No input file?");
  80.     ifile = args[optind];
  81.     if (! strcmp(ifile, "-"))
  82.         ft->fp = stdin;
  83.     else if ((ft->fp = fopen(ifile, READBINARY)) == NULL)
  84.         fail("Can't open input file '%s': %s", 
  85.             ifile, sys_errlist[errno]);
  86.     ft->filename = ifile;
  87.     optind++;
  88.  
  89.     /* Let -e allow no output file, just do an effect */
  90.     if (optind < n) {
  91.         if (strcmp(args[optind], "-e")) {
  92.         /* Get output format options */
  93.         ft = &outformat;
  94.         doopts(n, args);
  95.         /* Get output file */
  96.         if (optind >= n)
  97.             usage("No output file?");
  98.         ofile = args[optind];
  99.         ft->filename = ofile;
  100.         /*
  101.          * There are two choices here:
  102.          *    1) stomp the old file - normal shell "> file" behavior
  103.          *    2) fail if the old file already exists - csh mode
  104.          */
  105.         if (! strcmp(ofile, "-"))
  106.             ft->fp = stdout;
  107.         else {
  108. #ifdef    unix
  109.              /*    
  110.              * Remove old file if it's a text file, but 
  111.               * preserve Unix /dev/sound files.  I'm not sure
  112.              * this needs to be here, but it's not hurting
  113.              * anything.
  114.              */
  115.             if ((ft->fp = fopen(ofile, WRITEBINARY)) && 
  116.                    (filetype(fileno(ft->fp)) == S_IFREG)) {
  117.                 fclose(ft->fp);
  118.                 unlink(ofile);
  119.                 creat(ofile, 0666);
  120.                 ft->fp = fopen(ofile, WRITEBINARY);
  121.             }
  122. #else
  123.             ft->fp = fopen(ofile, WRITEBINARY);
  124. #endif
  125.             if (ft->fp == NULL)
  126.                 fail("Can't open output file '%s': %s", 
  127.                     ofile, sys_errlist[errno]);
  128. #ifdef    DOS
  129.             if (setvbuf (ft->fp,writebuf,_IOFBF,sizeof(writebuf)))
  130.                 fail("Can't set write buffer");
  131. #endif
  132.         }
  133.         writing = 1;
  134.         }
  135.         optind++;
  136.     }
  137.  
  138.     /* ??? */
  139. /*
  140.     if ((optind < n) && !writing && !eff.name)
  141.         fail("Can't do an effect without an output file!");
  142. */
  143.  
  144.     /* Get effect name */
  145.     if (optind < n) {
  146.         eff.name = args[optind];
  147.         optind++;
  148.         geteffect(&eff);
  149.         (* eff.h->getopts)(&eff, n - optind, &args[optind]);
  150.     }
  151.  
  152.     /* 
  153.      * If we haven't specifically set an output file 
  154.      * don't write a file; we could be doing a summary
  155.      * or a format check.
  156.      */
  157. /*
  158.     if (! ofile)
  159.         usage("Must be given an output file name");
  160. */
  161.     if (! ofile)
  162.         writing = 0;
  163.     /* Check global arguments */
  164.     if (volume <= 0.0)
  165.         fail("Volume must be greater than 0.0");
  166. #ifdef AMIGA
  167.     /* This only works so long as the assumption *
  168.      * that Amiga pipes are not used is valid... *
  169.      * In other words, don't use a pipe and      *
  170.      * you're okay.............................. */
  171. #ifndef TRUE
  172. #define TRUE 1
  173. #endif TRUE
  174.     informat.seekable  = TRUE;
  175.     outformat.seekable = TRUE;
  176.     
  177. #else
  178.     informat.seekable  = (filetype(fileno(informat.fp)) == S_IFREG);
  179.     outformat.seekable = (filetype(fileno(outformat.fp)) == S_IFREG); 
  180. #endif
  181.  
  182.     /* If file types have not been set with -t, set from file names. */
  183.     if (! informat.filetype) {
  184.         if (informat.filetype = strrchr(ifile, '/'))
  185.             informat.filetype++;
  186.         else
  187.             informat.filetype = ifile;
  188.         if (informat.filetype = strrchr(informat.filetype, '.'))
  189.             informat.filetype++;
  190.     }
  191.     if (writing && ! outformat.filetype) {
  192.         if (outformat.filetype = strrchr(ofile, '/'))
  193.             outformat.filetype++;
  194.         else
  195.             outformat.filetype = ofile;
  196.         if (outformat.filetype = strrchr(outformat.filetype, '.'))
  197.             outformat.filetype++;
  198.     }
  199.  
  200.     process();
  201.     statistics();
  202.     exit(0);
  203. }
  204.  
  205. doopts(n, args)
  206. int n;
  207. char **args;
  208. {
  209.     int c;
  210.     char *str;
  211.  
  212.     while ((c = getopt(n, args, "r:v:t:c:suUAbwlfdDxSV")) != -1) {
  213.         switch(c) {
  214.         case 't':
  215.             if (! ft) usage("-t");
  216.             ft->filetype = optarg;
  217.             if (ft->filetype[0] == '.')
  218.                 ft->filetype++;
  219.             break;
  220.  
  221.         case 'r':
  222.             if (! ft) usage("-r");
  223.             str = optarg;
  224.             if ((! sscanf(str, "%d", &ft->info.rate)) ||
  225.                     (ft->info.rate <= 0))
  226.                 fail("-r must be given a positive integer");
  227.             break;
  228.         case 'v':
  229.             if (! ft) usage("-v");
  230.             str = optarg;
  231.             if ((! sscanf(str, "%e", &volume)) ||
  232.                     (volume <= 0))
  233.                 fail("Volume value '%s' is not a number",
  234.                     optarg);
  235.             dovolume = 1;
  236.             break;
  237.  
  238.         case 'c':
  239.             if (! ft) usage("-c");
  240.             str = optarg;
  241.             if (! sscanf(str, "%d", &ft->info.channels))
  242.                 fail("-c must be given a number");
  243.             break;
  244.         case 'b':
  245.             if (! ft) usage("-b");
  246.             ft->info.size = BYTE;
  247.             break;
  248.         case 'w':
  249.             if (! ft) usage("-w");
  250.             ft->info.size = WORD;
  251.             break;
  252.         case 'l':
  253.             if (! ft) usage("-l");
  254.             ft->info.size = LONG;
  255.             break;
  256.         case 'f':
  257.             if (! ft) usage("-f");
  258.             ft->info.size = FLOAT;
  259.             break;
  260.         case 'd':
  261.             if (! ft) usage("-d");
  262.             ft->info.size = DOUBLE;
  263.             break;
  264.         case 'D':
  265.             if (! ft) usage("-D");
  266.             ft->info.size = IEEE;
  267.             break;
  268.  
  269.         case 's':
  270.             if (! ft) usage("-s");
  271.             ft->info.style = SIGN2;
  272.             break;
  273.         case 'u':
  274.             if (! ft) usage("-u");
  275.             ft->info.style = UNSIGNED;
  276.             break;
  277.         case 'U':
  278.             if (! ft) usage("-U");
  279.             ft->info.style = ULAW;
  280.             break;
  281.         case 'A':
  282.             if (! ft) usage("-A");
  283.             ft->info.style = ALAW;
  284.             break;
  285.         
  286.         case 'x':
  287.             if (! ft) usage("-x");
  288.             ft->swap = 1;
  289.             break;
  290.         
  291. /*  stat effect does this ?
  292.         case 'S':
  293.             summary = 1;
  294.             break;
  295. */
  296.         case 'V':
  297.             verbose = 1;
  298.             break;
  299.         }
  300.     }
  301. }
  302.  
  303. init() {
  304.  
  305.     /* init files */
  306.     informat.info.rate      = outformat.info.rate  = 0.0;
  307.     informat.info.size      = outformat.info.size  = -1;
  308.     informat.info.style     = outformat.info.style = -1;
  309.     informat.info.channels  = outformat.info.channels = -1;
  310.     informat.swap      = 0;
  311.     informat.filetype  = outformat.filetype  = (char *) 0;
  312.     informat.fp        = stdin;
  313.     outformat.fp       = stdout;
  314.     informat.filename  = "input";
  315.     outformat.filename = "output";
  316. }
  317.  
  318. /* 
  319.  * Process input file -> effect -> output file
  320.  *    one buffer at a time
  321.  */
  322.  
  323. process() {
  324.     long isamp, osamp, istart;
  325.     long i, idone, odone;
  326.  
  327.     gettype(&informat);
  328.     if (writing)
  329.         gettype(&outformat);
  330.     /* Read and write starters can change their formats. */
  331.     (* informat.h->startread)(&informat);
  332.     checkformat(&informat);
  333.     report("Input file: using sample rate %d\n\tsize %s, style %s, %d %s",
  334.         informat.info.rate, sizes[informat.info.size], 
  335.         styles[informat.info.style], informat.info.channels, 
  336.         (informat.info.channels > 1) ? "channels" : "channel");
  337.     if (writing) {
  338.         copyformat(&informat, &outformat);
  339.         (* outformat.h->startwrite)(&outformat);
  340.         checkformat(&outformat);
  341.         cmpformats(&informat, &outformat);
  342.     report("Output file: using sample rate %d\n\tsize %s, style %s, %d %s",
  343.         outformat.info.rate, sizes[outformat.info.size], 
  344.         styles[outformat.info.style], outformat.info.channels, 
  345.         (outformat.info.channels > 1) ? "channels" : "channel");
  346.     }
  347.     /* Very Important: 
  348.      * Effect fabrication and start is called AFTER files open.
  349.      * Effect may write out data beforehand, and
  350.      * some formats don't know their sample rate until now.
  351.      */
  352.     checkeffect();
  353.     /* inform effect about signal information */
  354.     eff.ininfo = informat.info;
  355.     eff.outinfo = outformat.info;
  356.     (* eff.h->start)(&eff);
  357.     istart = 0;
  358.     while((isamp = (*informat.h->read)(&informat,&ibuf[istart],
  359.             (long) BUFSIZ-istart))>0) {
  360.         long *ib = ibuf;
  361.  
  362.         isamp += istart;
  363.         /* Do volume before effect or after?  idunno */
  364.         if (dovolume) for (i = 0; i < isamp; i++)
  365.             ibuf[i] = volumechange(ibuf[i]);
  366.         osamp = sizeof(obuf) / sizeof(long);
  367.         /* Effect (i.e. rate change) may do different sizes I and O */
  368.         while (isamp) {
  369.             idone = isamp;
  370.             odone = osamp;
  371.             (* eff.h->flow)(&eff, ib, obuf, &idone, &odone);
  372.             /* 
  373.              * kludge!     
  374.              * Effect is stuck.  Start over with new buffer.
  375.              * This can drop samples at end of file. 
  376.              * No effects currently do this, but it could happen.
  377.              */
  378.             if (idone == 0) {
  379.                 int i;
  380.                 for(i = isamp - 1; i; i--)
  381.                     ibuf[i] = ib[i];
  382.                 istart = isamp;
  383.                 isamp = 0;
  384.                 break;
  385.             }
  386.             if (writing) 
  387.                 (* outformat.h->write)(&outformat, obuf, (long) odone);
  388.             isamp -= idone;
  389.             ib += idone;
  390.         }
  391.     }
  392.     /* Drain effect out */
  393.     if (writing) {
  394.         odone = sizeof(obuf) / sizeof(long);
  395.         (* eff.h->drain)(&eff, obuf, &odone);
  396.         if (odone > 0)
  397.             (* outformat.h->write)(&outformat, obuf, (long) odone);
  398.         /* keep calling it until it returns a partial buffer */
  399.         while (odone == (sizeof(obuf) / sizeof(long))) {
  400.             (* eff.h->drain)(&eff, obuf, &odone);
  401.             if (odone)
  402.              (* outformat.h->write)(&outformat, obuf, (long) odone);
  403.         }
  404.     }
  405.     /* Very Important: 
  406.      * Effect stop is called BEFORE files close.
  407.      * Effect may write out more data after. 
  408.      */
  409.     (* eff.h->stop)(&eff);
  410.     (* informat.h->stopread)(&informat);
  411.     fclose(informat.fp);
  412.     if (writing)
  413.         (* outformat.h->stopwrite)(&outformat);
  414.     if (writing)
  415.         fclose(outformat.fp);
  416. }
  417.  
  418. /*
  419.  * Check that we have a known format suffix string.
  420.  */
  421. void
  422. gettype(formp)
  423. ft_t formp;
  424. {
  425.     char **list;
  426.     int i;
  427.     extern format_t formats[];
  428.  
  429.     if (! formp->filetype)
  430. fail("Must give file type for %s file, either as suffix or with -t option",
  431. formp->filename);
  432.     for(i = 0; formats[i].names; i++) {
  433.         for(list = formats[i].names; *list; list++) {
  434.             char *s1 = *list, *s2 = formp->filetype;
  435.             if (! strcmpcase(s1, s2))
  436.                 break;    /* not a match */
  437.         }
  438.         if (! *list)
  439.             continue;
  440.         /* Found it! */
  441.         formp->h = &formats[i];
  442.         return;
  443.     }
  444.     if (! strcmpcase(formp->filetype, "snd")) {
  445.         verbose = 1;
  446.         report("File type '%s' is used to name several different formats.", formp->filetype);
  447.         report("If the file came from a Macintosh, it is probably");
  448.         report("a .ub file with a sample rate of 11025 (or possibly 5012 or 22050).");
  449.         report("Use the sequence '-t .ub -r 11025 file.snd'");
  450.         report("If it came from a PC, it's probably a Soundtool file.");
  451.         report("Use the sequence '-t .sndt file.snd'");
  452.         report("If it came from a NeXT, it's probably a .au file.");
  453.         fail("Use the sequence '-t .au file.snd'\n");
  454.     }
  455.     fail("File type '%s' of %s file is not known!",
  456.         formp->filetype, formp->filename);
  457. }
  458.  
  459. copyformat(ft, ft2)
  460. ft_t ft, ft2;
  461. {
  462.     int noise = 0;
  463.     if (ft2->info.rate == 0.0) {
  464.         ft2->info.rate = ft->info.rate;
  465.         noise = 1;
  466.     }
  467.     if (outformat.info.size == -1) {
  468.         ft2->info.size = ft->info.size;
  469.         noise = 1;
  470.     }
  471.     if (outformat.info.style == -1) {
  472.         ft2->info.style = ft->info.style;
  473.         noise = 1;
  474.     }
  475.     if (outformat.info.channels == -1) {
  476.         ft2->info.channels = ft->info.channels;
  477.         noise = 1;
  478.     }
  479.     return noise;
  480. }
  481.  
  482. cmpformats(ft, ft2)
  483. ft_t ft, ft2;
  484. {
  485.     int noise = 0;
  486.     float abs;
  487.  
  488. }
  489.  
  490. /* check that all settings have been given */
  491. checkformat(ft) 
  492. ft_t ft;
  493. {
  494.     if (ft->info.rate == 0.0)
  495.         fail("Sampling rate for %s file was not given\n", ft->filename);
  496.     if ((ft->info.rate < 100) || (ft->info.rate > 50000))
  497.         fail("Sampling rate %d for %s file is bogus\n", 
  498.             ft->info.rate, ft->filename);
  499.     if (ft->info.size == -1)
  500.         fail("Data size was not given for %s file\nUse one of -b/-w/-l/-f/-d/-D", ft->filename);
  501.     if (ft->info.style == -1)
  502.         fail("Data style was not given for %s file\nUse one of -s/-u/-U/-A", ft->filename);
  503.     /* it's so common, might as well default */
  504.     if (ft->info.channels == -1)
  505.         ft->info.channels = 1;
  506.     /*    fail("Number of output channels was not given for %s file",
  507.             ft->filename); */
  508. }
  509.  
  510. /*
  511.  * If no effect given, decide what it should be.
  512.  */
  513. checkeffect(effp)
  514. eff_t effp;
  515. {
  516.     int already = (eff.name != (char *) 0);
  517.     char *rate = 0, *chan = 0;
  518.     int i;
  519.  
  520.     for (i = 0; effects[i].name; i++) {
  521.         if (!chan && (effects[i].flags & EFF_CHAN))
  522.             chan = effects[i].name;
  523.         if (! rate && (effects[i].flags & EFF_RATE))
  524.             rate = effects[i].name;
  525.     }
  526.  
  527.     if (eff.name && ! writing)
  528.         return;
  529.  
  530.     /* 
  531.      * Require mixdown for channel mismatch.
  532.      * XXX Doesn't handle channel expansion.  Need an effect for this.
  533.      * Require one of the rate-changers on a rate change.
  534.      * Override a rate change by explicitly giving 'copy' command.
  535.      */
  536.     if (informat.info.channels != outformat.info.channels) {
  537.         if (eff.name && !(eff.h->flags & EFF_CHAN))
  538.             fail("Need to do change number of channels first.  Try the '%s' effect.", chan);
  539.         if (! eff.name) {
  540.             eff.name = chan;
  541.             report(
  542. "Changing %d input channels to %d output channels with '%s' effect\n",
  543.             informat.info.channels, outformat.info.channels, chan);
  544.             geteffect(&eff);
  545.         }
  546.     } 
  547.     /* 
  548.      * Be liberal on rate difference errors.
  549.      * Note that the SPARC 8000-8192 problem
  550.      * comes in just under the wire.  XXX
  551.      *
  552.       * Bogus.  Should just do a percentage.
  553.      */
  554.     if (abs(informat.info.rate - outformat.info.rate) > 200) {
  555.         if (eff.name && !(eff.h->flags & EFF_RATE))
  556.             fail("Need to do rate change first.  Try the '%s' effect.", 
  557.             rate);
  558.         if (! eff.name) {
  559.             eff.name = rate;
  560.             report(
  561. "Changing sample rate %d to rate %d via noisy 'rate' effect\n",
  562.             informat.info.rate, outformat.info.rate);
  563.             geteffect(&eff);
  564.         }
  565.     }
  566.     /* don't need to change anything */
  567.     if (! eff.name)
  568.         eff.name = "copy";
  569.     if (! already) {
  570.         geteffect(&eff);
  571.         /* give default opts for manufactured effect */
  572.         (eff.h->getopts)(&eff, 0, (char *) 0);
  573.     }
  574. }
  575.  
  576. /*
  577.  * Check that we have a known effect name.
  578.  */
  579. void
  580. geteffect(effp)
  581. eff_t effp;
  582. {
  583.     int i;
  584.  
  585.     for(i = 0; effects[i].name; i++) {
  586.         char *s1 = effects[i].name, *s2 = effp->name;
  587.         while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
  588.             s1++, s2++;
  589.         if (*s1 || *s2)
  590.             continue;    /* not a match */
  591.         /* Found it! */
  592.         effp->h = &effects[i];
  593.         return;
  594.     }
  595.     /* Guido Van Rossum fix */
  596.     fprintf(stderr, "Known effects:");
  597.     for (i = 0; effects[i].name; i++)
  598.         fprintf(stderr, "\t%s\n", effects[i].name);
  599.     fail("\nEffect '%s' is not known!", effp->name);
  600. }
  601.  
  602. /* Guido Van Rossum fix */
  603. statistics() {
  604.     if (dovolume && clipped > 0)
  605.         report("Volume change clipped %d samples", clipped);
  606. }
  607.  
  608. long volumechange(y)
  609. long y;
  610. {
  611.     double y1;
  612.  
  613.     y1 = y * volume;
  614.     if (y1 < -2147483647.0) {
  615.         y1 = -2147483647.0;
  616.         clipped++;
  617.     }
  618.     else if (y1 > 2147483647.0) {
  619.         y1 = 2147483647.0;
  620.         clipped++;
  621.     }
  622.  
  623.     return y1;
  624. }
  625.  
  626. #ifndef AMIGA
  627. filetype(fd)
  628. int fd;
  629. {
  630.     struct stat st;
  631.  
  632.     fstat(fd, &st);
  633.  
  634.     return st.st_mode & S_IFMT;
  635. }
  636. #endif        /* Amiga has no native fstat.  filetype() is unneeded *
  637.          * as far as Amiga soundkit is concerned.  Ergo, kill *
  638.          * this stuff. */
  639.  
  640. char *usagestr = 
  641. "[ -V -S ] [ fopts ] ifile [ fopts ] ofile [ effect [ effopts ] ]\nfopts: -r rate -v volume -c channels -s/-u/-U/-A -b/-w/-l/-f/-d/-D -x\neffects and effopts: various";
  642.  
  643. usage(opt)
  644. char *opt;
  645. {
  646. #ifndef    DOS
  647.     /* single-threaded machines don't really need this */
  648.     fprintf(stderr, "%s: ", myname);
  649. #endif
  650.     fprintf(stderr, "Usage: %s", usagestr);
  651.     if (opt)
  652.         fprintf(stderr, "\nFailed at: %s\n", opt);
  653.     exit(1);
  654. }
  655.  
  656. void
  657. report(va_alist) 
  658. va_dcl
  659. {
  660.     va_list args;
  661.     char *fmt;
  662.  
  663.     if (! verbose)
  664.         return;
  665. #ifndef    unix
  666.     /* single-threaded machines don't really need this */
  667.     fprintf(stderr, "%s: ", myname);
  668. #endif
  669.     va_start(args);
  670.     fmt = va_arg(args, char *);
  671.     vfprintf(stderr, fmt, args);
  672.     va_end(args);
  673.     fprintf(stderr, "\n");
  674. }
  675.  
  676. fail(va_alist) 
  677. va_dcl
  678. {
  679.     va_list args;
  680.     char *fmt;
  681.  
  682. #ifndef    unix
  683.     /* single-threaded machines don't really need this */
  684.     fprintf(stderr, "%s: ", myname);
  685. #endif
  686.     va_start(args);
  687.     fmt = va_arg(args, char *);
  688.     vfprintf(stderr, fmt, args);
  689.     va_end(args);
  690.     fprintf(stderr, "\n");
  691.     exit(2);
  692. }
  693.  
  694.  
  695. strcmpcase(s1, s2)
  696. char *s1, *s2;
  697. {
  698.     while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
  699.         s1++, s2++;
  700.     return *s1 - *s2;
  701. }
  702.